package com.xiyu.service.framework.postOffice.protocol;

import com.xiyu.service.model.mailbox.account.MailboxAccount;
import jakarta.mail.*;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.util.SharedByteArrayInputStream;
import org.eclipse.angus.mail.pop3.POP3Folder;
import org.eclipse.angus.mail.pop3.POP3Store;

import java.util.*;

import static com.xiyu.service.errorCode.mailbox.PostOfficeErrorCode.*;
import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;

public class PopProtocol implements ReceiveProtocol {
    private final POP3Store store;

    private final Session session;

    private Authenticator getAuth(String account, String password) {
        return new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(account, password);
            }
        };
    }

    public PopProtocol(MailboxAccount account) {
        Properties props = new Properties();
        props.put("mail.store.protocol", "pop3");
        props.put("mail.pop3.host", account.receiveAddress());
        props.put("mail.pop3.port", account.receivePort());
        props.put("mail.pop3.ssl.enable", account.isReceiveSecure());

        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", account.isStarttls());
        props.put("mail.smtp.ssl.enable", account.isSendSecure());
        props.put("mail.smtp.host", account.sendAddress());
        props.put("mail.smtp.port", account.sendPort());

        try {
            session = Session.getInstance(props, getAuth(account.mailboxAccount(), account.mailboxPassword()));

            store = (POP3Store) session.getStore("pop3");
            store.connect();
        } catch (AuthenticationFailedException e) {
            throw exception(POST_OFFICE_PASSWORD_ERROR);

        } catch (MessagingException e) {
            throw exception(POST_OFFICE_CONNECT_ERROR);
        }
    }

    @Override
    public void openFolder(Folder postOfficeFolder, int mode) {
        if(postOfficeFolder == null)
            return;
        try {
            POP3Folder folder = (POP3Folder) postOfficeFolder;
            if (!folder.isOpen())
                folder.open(mode);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_OPEN_FOLDER_ERROR);
        }
    }

    @Override
    public void closeFolder(Folder postOfficeFolder, boolean expunge) {
        if(postOfficeFolder == null)
            return;
        try {
            POP3Folder folder = (POP3Folder) postOfficeFolder;
            if(folder.isOpen())
                folder.close(expunge);
        }catch (MessagingException e) {
            e.printStackTrace();
            throw exception(POST_OFFICE_CLOSE_FOLDER_ERROR);
        }
    }

    @Override
    public void setMessagesFlag(Folder postOfficeFolder, Message[] messages, Flags flags, boolean isTrue) {

    }

    @Override
    public Message[] getMessagesByUid(Folder postOfficeFolder, String[] uid) {
        return null;
    }

    @Override
    public long[] moveMessages(Folder sourcePostOfficeFolder, Folder targetPostOfficeFolder, Message[] messages) {
        throw exception(POST_OFFICE_POP_NOT_MOVE_MAIL);
    }

    @Override
    public void deleteMessages(Folder postOfficeFolder, Message[] messages) {
        throw exception(POST_OFFICE_POP_NOT_DELETE_MAIL);
    }

    @Override
    public Message createMessage() {
        return new MimeMessage(session);
    }

    @Override
    public Message createMessage(SharedByteArrayInputStream inputStream) throws MessagingException {
        return new MimeMessage(session, inputStream);
    }

    @Override
    public String getNextUid(Folder postOfficeFolder) {
        return "";
    }

    @Override
    public List<Folder> getAllFolder() {
        try {
            return Arrays.asList(store.getDefaultFolder().list());
        }catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_DEFAULT_FOLDER_ERROR);
        }
    }

    @Override
    public String createFolder(String parentFolderName, String newFolderName){
        throw exception(POST_OFFICE_POP_NOT_HANDLE_FOLDER);
    }

    @Override
    public String renameFolder(String parentFolderName, String folderName, String newFolderName) {
        throw exception(POST_OFFICE_POP_NOT_HANDLE_FOLDER);
    }

    @Override
    public void deleteFolder(String folderName) {
        throw exception(POST_OFFICE_POP_NOT_HANDLE_FOLDER);
    }

    @Override
    public Folder getRootFolder() {
        try {
            return store.getDefaultFolder();
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_FOLDER_ERROR);
        }
    }

    @Override
    public String getFolderName(Folder postOfficeFolder) {
        return postOfficeFolder.getFullName();
    }

    @Override
    public String[] getFolderAttributes(Folder postOfficeFolder) {
        return new String[0];
    }

    @Override
    public LinkedHashMap<String, Message> getFolderMessages(Folder postOfficeFolder, int begin, int end) {
        try{
            POP3Folder folder = (POP3Folder)postOfficeFolder;
            int messageCount = folder.getMessageCount();
            Message[] messages = folder.getMessages(begin, Math.min(end, messageCount));
            return batchGetUidMessage(folder, messages);
        } catch (Exception e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    private LinkedHashMap<String, Message> batchGetUidMessage(POP3Folder folder, Message[] messages){
        try {
            LinkedHashMap<String, Message> messageMap = new LinkedHashMap<>();
            for(Message message : messages){
                String uid = folder.getUID(message);
                messageMap.put(uid, message);
            }
            return messageMap;
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public Folder getFolderByName(String folderName) {
        if(folderName.isEmpty())
            return null;
        try {
            return store.getFolder(folderName);
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_GET_FOLDER_ERROR);
        }
    }

    @Override
    public int getFolderMailCount(Folder postOfficeFolder) {
        try {
            POP3Folder folder = (POP3Folder)postOfficeFolder;
            return folder.getMessageCount();
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public int getFolderUnreadMailCount(Folder postOfficeFolder) {
        try {
            POP3Folder folder = (POP3Folder)postOfficeFolder;
            return folder.getUnreadMessageCount();
        } catch (MessagingException e) {
            throw exception(POST_OFFICE_RECEIVE_FOLDER_MAIL_ERROR);
        }
    }

    @Override
    public int getFolderTopMailCount(Folder postOfficeFolder) {
        return 0;
    }

    @Override
    public void testGetMessage() {
    }

    @Override
    public LinkedHashMap<String, Message> getFolderUnreadMessages(Folder postOfficeFolder) {
        return null;
    }

    @Override
    public LinkedHashMap<String, Message> getFolderTopMessages(Folder postOfficeFolder) {
        return null;
    }

    @Override
    public void close() {
        try {
            store.close();
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

}
